﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using Newtonsoft.Json;

namespace FBGraph
{
    /// <summary>Represents errors that are thrown by the Graph API</summary>
    public class FBGraphApiException : Exception
    {
        /// <summary>An internal class for reading exception data from the HTTP response.</summary>
        private class ExceptionData
        {
            public static ExceptionData LoadFromResponse(WebResponse response)
            {
                using (var stream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(stream))
                    {
                        var content = reader.ReadToEnd();
                        var json = JsonConvert.DeserializeObject<Dictionary<String, ExceptionData>>(content);
                        if (json != null)
                        {
                            var data = json["error"];
                            return data;
                        }
                        else return null;
                    }
                }
            }

            public String Type { get; set; }
            public String Message { get; set; }
        }

        /// <summary>Initializes an instance of <see cref="FBGraphApiException" /> using the specified <see cref="WebException" /> for content.</summary>
        /// <param name="ex">A <see cref="WebException" /> that was thrown while attempting to make a request to the Graph API.</param>
        internal FBGraphApiException(WebException ex)
            : this(ExceptionData.LoadFromResponse(ex.Response)) { }

        private FBGraphApiException(ExceptionData data)
            : base(data == null ? "The server returned an error, but no error message was provided" : String.Format("{0}: {1}", data.Type, data.Message))
        {
            if (data != null)
            {
                this.Type = data.Type;
            }            
        }

        /// <summary>Gets the type of exception thrown by the Graph API.</summary>
        public String Type { get; private set; }

        /// <summary>Gets the <see cref="FBGraphApiErrorType" /> of the exception.</summary>
        public FBGraphApiErrorType ErrorType { get; private set; }

        private FBGraphApiErrorType GetErrorType()
        {
            var result = errorTypeAttributes.SingleOrDefault(a => a.Value.Message == this.Message);
            if (result.Equals(default(KeyValuePair<FBGraphApiErrorType, FBGraphApiErrorAttribute>))) return FBGraphApiErrorType.Unknown;
            else return result.Key;
        }

        private static readonly Dictionary<FBGraphApiErrorType, FBGraphApiErrorAttribute> errorTypeAttributes = GetErrorTypeAttributes();

        private static Dictionary<FBGraphApiErrorType, FBGraphApiErrorAttribute> GetErrorTypeAttributes()
        {
            var query =
                from field in typeof(FBGraphApiErrorType).GetFields()
                where field.GetAttribute<FBGraphApiErrorAttribute>() != null
                select new
                {
                    ErrorType = (FBGraphApiErrorType)field.GetValue(null),
                    Attribute = (FBGraphApiErrorAttribute)field.GetAttribute<FBGraphApiErrorAttribute>()
                };

            return query.ToDictionary(i => i.ErrorType, i => i.Attribute);
        }
    }
}